package com.hls.mybatis.core.database;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.hls.mybatis.util.StringUtil;


public class MapperSupport {

    
    /**
     * return as list for T 
     */
    @SuppressWarnings("rawtypes")
    public static <T> List<T> selectForBeanList(Class clazz,String sql,Object...objects)
    {
        List<T> list = new ArrayList<T>();
        List<Map<String,Object>> mapList = queryForMapList(sql,objects);
        for(Map<String,Object> map:mapList)
        {
            list.add(queryForBean(map, clazz));
        }
        return list;
    }
    
    
    /**
     * return T 
     */
    @SuppressWarnings("rawtypes")
    public static <T>T selectForBean(Class clazz,String sql,Object...objects)
    {
        Map<String,Object> map = queryForMap(sql,objects);
        
        return queryForBean(map,clazz);
    }
    
    
    /**
     * except select method use this method
     * @param updateSQL
     * @param objects
     * @return
     */
    public static int update(String updateSQL,Object...objects)
    {
        Connection conn = DBHelper.getConnection();
        PreparedStatement pStam = null;
        int result = 10;
        try
        {
            pStam = DBHelper.prepareStatement(conn, updateSQL);  
            for(int i=0;i<objects.length;i++)
            {
                pStam.setObject(i+1, objects[i]);
            }
            result = pStam.executeUpdate();
        } catch (SQLException e)
        {
            e.printStackTrace();
        }finally
        {
            DBHelper.closePreparedStatement(pStam);
            DBHelper.closeConnection(conn);
        }
        return result;
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static <T>T queryForBean(Map<String,Object> map,  Class clazz)
    {
        T obj = null;
        Field field = null;
        try
        {
            obj = (T) clazz.newInstance(); 
        } catch (InstantiationException e)
        {
            e.printStackTrace();
        } catch (IllegalAccessException e)
        {
            e.printStackTrace();
        }
        if(map == null)                              
            return null;
        for(String columnName:map.keySet())
        {
            Method method = null;
            
            String propertyName = StringUtil.columnToProperty(columnName.toLowerCase()); 

            try
            {
                field = clazz.getDeclaredField(propertyName);
            } catch (NoSuchFieldException e)
            {
                e.printStackTrace();
            } catch (SecurityException e)
            {
                e.printStackTrace();
            }
            String fieldType = field.toString().split(" ")[1]; 
            Object value = map.get(columnName);              
            if(value == null)
            {
                continue;
            }
            String setMethodName ="set"+StringUtil.upperCaseFirstCharacter(propertyName);
            String valueType = value.getClass().getName();  
            
            if(!fieldType.equalsIgnoreCase(valueType))
            {
                if(fieldType.equalsIgnoreCase("java.lang.Integer"))
                {
                    value =Integer.parseInt(String.valueOf(value));
                }
                else if(fieldType.equalsIgnoreCase("java.lang.String"))
                {
                    value = String.valueOf(value);
                }
                else if(fieldType.equalsIgnoreCase("java.util.Date"))
                {
                    valueType = "java.lang.Date";
                    String datestr = String.valueOf(value);
                    Timestamp ts = Timestamp.valueOf(datestr);
                    Date date = new Date(ts.getTime());
                    value = date;
                }
            }
            try
            {
                method = clazz.getDeclaredMethod(setMethodName, Class.forName(fieldType));
                method.invoke(obj, value);
            } catch (NoSuchMethodException e)
            {
                e.printStackTrace();
            } catch (SecurityException e)
            {
                e.printStackTrace();
            } catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            } catch (IllegalAccessException e)
            {
                e.printStackTrace();
            } catch (IllegalArgumentException e)
            {
                e.printStackTrace();
            } catch (InvocationTargetException e)
            {
                e.printStackTrace();
            }
            
        }
        return obj;
    }
    /**
     * return single T if not found or has many result then return null
     * @param sql
     * @param objects
     * @return
     */
    private static Map<String,Object> queryForMap(String sql,Object...objects)
    {
        Map<String,Object> result = new HashMap<String,Object>();
        Connection conn = DBHelper.getConnection();  
        PreparedStatement pStam = null;
        ResultSet rs = null;
        try
        {
            pStam = DBHelper.prepareStatement(conn, sql); 
            if(objects!=null)
            {
                for(int i=0;i<objects.length;i++) 
                {
                    pStam.setObject(i+1, objects[i]);
                }
            }
            
            rs = pStam.executeQuery();
            
            if(rs.next())
            {
                
                ResultSetMetaData rsmd = rs.getMetaData();
                int count = rsmd.getColumnCount();
                for(int i=0;i<count;i++)
                {
                    result.put(rsmd.getColumnName(i+1), rs.getObject(rsmd.getColumnName(i+1)));
                }
                if(rs.next())
                    result = null;
                
            }
        } catch (SQLException e)
        {
            e.printStackTrace();
        }finally
        {
            DBHelper.closeResultSet(rs);
            DBHelper.closePreparedStatement(pStam);
            DBHelper.closeConnection(conn);
        }
        return result;
    }
    
    
    /**
     * return List as Map
     * @param sql
     * @param objects
     * @return
     */
    private static List<Map<String,Object>> queryForMapList(String sql,Object...objects)
    {
        List<Map<String,Object>> result = new ArrayList<Map<String,Object>>();
        Connection conn = DBHelper.getConnection();
        PreparedStatement pStam = null;
        ResultSet rs = null;
        try
        {
            pStam = DBHelper.prepareStatement(conn, sql);  
            for(int i=0;i<objects.length;i++)
            {
                pStam.setObject(i+1, objects[i]);
            }
            rs = pStam.executeQuery();
            while(rs.next())
            {
                ResultSetMetaData rsmd = rs.getMetaData(); 
                int count = rsmd.getColumnCount();      
                Map<String,Object> map = new HashMap<String,Object>();
                for(int i=0;i<count;i++)
                {
                    map.put(rsmd.getColumnName(i+1), rs.getObject(rsmd.getColumnName(i+1)));
                }
                result.add(map);
            }
            
        } catch (SQLException e)
        {
            e.printStackTrace();
        }finally
        {
            DBHelper.closeResultSet(rs);
            DBHelper.closePreparedStatement(pStam);
            DBHelper.closeConnection(conn);
        }
        return result;
    }

}